module utils.Memory;

private {
	import std.c.stdlib : cMalloc = malloc, cRealloc = realloc, cFree = free;
}



uint	mallocdMemory;



void alloc(T, intT)(inout T array, intT numItems, bool init = true) 
in {
	assert (numItems >= 0);
}
out {
	assert (numItems == array.length);
}
body {
	alias typeof(T[0]) ItemT;
	array = (cast(ItemT*)cMalloc(ItemT.sizeof * numItems))[0 .. numItems];
	mallocdMemory += ItemT.sizeof * numItems;
	static if (is(typeof(ItemT.init))) {
		if (init) {
			array[] = ItemT.init;
		}
	}
}


void realloc(T, intT)(inout T array, intT numItems, bool init = true)
in {
	assert (numItems >= 0);
}
out {
	assert (numItems == array.length);
}
body {
	alias typeof(T[0]) ItemT;
	intT oldLen = array.length;
	array = (cast(ItemT*)cRealloc(array.ptr, ItemT.sizeof * numItems))[0 .. numItems];
	mallocdMemory += ItemT.sizeof * (numItems - oldLen);
	static if (is(typeof(ItemT.init))) {
		if (init && numItems > oldLen) {
			array[oldLen .. numItems] = ItemT.init;
		}
	}
	assert (numItems == array.length);
}


void free(T)(inout T array)
out {
	assert (0 == array.length);
}
body {
	mallocdMemory -= T[0].sizeof * array.length;
	cFree(array.ptr);
	array = null;
}


void append(T, I)(inout T array, I elem, uint* realLength = null) {
	uint len = realLength is null ? array.length : *realLength;
	uint capacity = array.length;
	
	if (len >= capacity) {
		if (realLength is null) {		// just add one element to the array
			array.realloc(len+1, false);
		} else {			// be smarter and allocate in power-of-two increments
			const uint initialCapacity = 4;
			array.realloc(capacity == 0 ? initialCapacity : capacity * 2, false);
			++*realLength;
		}
	} else if (realLength !is null) ++*realLength;
	
	array[len] = elem;
}



unittest {
	int[] foo;
	foo.alloc(10);
	assert (foo.length == 10);
	foreach (i; foo) assert (i == int.init);
	foo.realloc(20);
	assert (foo.length == 20);
	foreach (i; foo) assert (i == int.init);
	foo.realloc(10);
	assert (foo.length == 10);
	foreach (i; foo) assert (i == int.init);
	foo.free();
	
	
	uint barLen = 0;
	int[] bar;
	append(bar, 10, &barLen);
	append(bar, 20, &barLen);
	append(bar, 30, &barLen);
	append(bar, 40, &barLen);
	
	assert (bar.length == 16);
	assert (barLen == 4);
	
	for (int i = 0; i < 20; ++i) {
		append(bar, i, &barLen);
	}
	
	assert (bar.length == 32);
	assert (barLen == 24);
	
	assert (bar[0 .. 4] == [1, 2, 3, 4]);
}